/*
 *  Copyright (c) 2010 Stephen Nelson <stephen@sfnelson.org>
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *  THE SOFTWARE.
 */

package org.sfnelson.bma.server;

import java.math.BigInteger;
import java.nio.charset.Charset;

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

import org.bouncycastle.crypto.digests.SHA1Digest;

import authenticator.Util;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;

@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable="true")
public class SecretStore {

    private static final PersistenceManagerFactory PMF =
            JDOHelper.getPersistenceManagerFactory("transactions-optional");

    public static SecretStore getStore(String userId) {
        Key key = KeyFactory.createKey(SecretStore.class.getSimpleName(), userId);

        SecretStore store = null;

        PersistenceManager pm = PMF.getPersistenceManager();
        try {
            store = pm.getObjectById(SecretStore.class, key);
        } catch (Exception ex) {
        } finally {
            pm.close();
        }

        return store;
    }

    public static void setSecret(String userId, String serial, byte[] token, long timeOffset) {
        Key key = KeyFactory.createKey(SecretStore.class.getSimpleName(), userId);

        PersistenceManager pm = PMF.getPersistenceManager();
        pm.setDetachAllOnCommit(true);

        SecretStore store = null;
        try {
            store = pm.getObjectById(SecretStore.class, key);
        } catch (Exception ex) {}

        if (store == null) {
            store = new SecretStore(key, pm);
        }

        store.serial = serial;
        store.token = new BigInteger(token).toString(16);
        store.timeOffset = timeOffset;
        pm.close();
    }

    public SecretStore(Key key, PersistenceManager pm) {
        this.key = key;

        pm.makePersistent(this);
    }

    @SuppressWarnings("unused")
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;

    @Persistent
    private String serial;

    @Persistent
    private String token;

    @Persistent
    private Long timeOffset;

    String getSerial() {
        return serial;
    }

    byte[] getToken() {
        return new BigInteger(token, 16).toByteArray();
    }

    String getTokenString() {
        return token;
    }

    Long getTimeOffset() {
        return timeOffset;
    }

    String buildRestoreCode() {

        // get byte array of serial
        byte[] serialdata = serial.toUpperCase().replace("-", "")
                .getBytes(Charset.forName("UTF-8"));

        byte[] tok = getToken();

        // combine serial data and secret data
        byte[] combined = new byte[serialdata.length + tok.length];
        System.arraycopy(serialdata, 0, combined, 0, serialdata.length);
        System.arraycopy(tok, 0, combined, serialdata.length, tok.length);

        // create digest of combined data
        SHA1Digest digest = new SHA1Digest();
        digest.update(combined, 0, combined.length);
        byte[] digestdata = new byte[digest.getDigestSize()];
        digest.doFinal(digestdata, 0);

        // take last 10 chars of hash and convert each byte to our encoded
        // string that doesn't use I,L,O,S
        StringBuilder code = new StringBuilder();
        int startpos = digestdata.length - 10;
        for (int i = 0; i < 10; i++) {
            code.append(Util.convertRestoreCodeByteToChar(digestdata[startpos + i]));
        }

        return code.toString();
    }
}
